/**********************\
* Code: Dzlua
* QQ:505544956
* Time:2017/05/17
\**********************/
#include "common.h"

#include <Windows.h>
#include <Shellapi.h>

#pragma comment(lib, "Shell32.lib")

#include <modp_b64/modp_b64.h>
#include <sstream>
#include <fstream>

// run cmd
bool Common::ExecuteShell(const std::string& exe, const std::string& args, bool wait) {
    SHELLEXECUTEINFOA sei = { sizeof(SHELLEXECUTEINFOA) };  
  
    sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;  
  
    sei.lpFile = exe.c_str();  
    sei.lpParameters = args.c_str();  
    sei.lpDirectory = nullptr;  
  
    if (!::ShellExecuteExA(&sei)) {  
        return false;  
    }  
  
    if (wait) {  
        HANDLE hProcess = sei.hProcess;  
        if (hProcess != 0) {  
            ::WaitForSingleObject(hProcess, INFINITE);  
            ::CloseHandle(hProcess);  
        }  
    }  
  
    return true; 
}

bool Common::ExecuteShell(const std::wstring& exe, const std::wstring& args, bool wait) {
    SHELLEXECUTEINFOW sei = { sizeof(SHELLEXECUTEINFOW) };  
  
    sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI;  
  
    sei.lpFile = exe.c_str();  
    sei.lpParameters = args.c_str();  
    sei.lpDirectory = nullptr;  
  
    if (!::ShellExecuteExW(&sei)) {  
        return false;  
    }  
  
    if (wait) {  
        HANDLE hProcess = sei.hProcess;  
        if (hProcess != 0) {  
            ::WaitForSingleObject(hProcess, INFINITE);  
            ::CloseHandle(hProcess);  
        }  
    }  
  
    return true; 
}

/* if file not exist, return empty.*/
std::string Common::GetShortPath(const std::string& file) {
    char sp [MAX_PATH];
    long len = ::GetShortPathNameA(file.c_str(), sp, MAX_PATH);
    if (len <= 0) return std::string();
    return std::move(std::string(sp, len));
}

std::wstring Common::GetShortPath(const std::wstring& file) {
    wchar_t sp [MAX_PATH];
    long len = ::GetShortPathNameW(file.c_str(), sp, MAX_PATH);
    if (len <= 0) return std::wstring();
    return std::move(std::wstring(sp, len));
}

/* place the give str at begin and end of src 
    src:"aaa",str:"."->".aaa."
*/
std::string Common::AroundMarks(const std::string& str, const std::string& mark) {
    return std::move(std::string(mark + str + mark));
}

std::wstring Common::AroundMarks(const std::wstring& str, const std::wstring& mark) {
    return std::move(std::wstring(mark + str + mark));
}

// split the string s with c,save them the result at v.
void Common::Split(const std::string& s, std::vector<std::string>& v, const std::string& c) {
    std::string::size_type pos1, pos2;
    pos2 = s.find(c);
    pos1 = 0;
    v.clear();
    while(std::string::npos != pos2) {
        v.push_back(s.substr(pos1, pos2 - pos1));
    
        pos1 = pos2 + c.size();
        pos2 = s.find(c, pos1);
    }
    if(pos1 != s.length())
        v.push_back(s.substr(pos1));
}

void Common::Split(const std::wstring& s, std::vector<std::wstring>& v, const std::wstring& c) {
    std::wstring::size_type pos1, pos2;
    pos2 = s.find(c);
    pos1 = 0;
    v.clear();
    while(std::wstring::npos != pos2) {
        v.push_back(s.substr(pos1, pos2 - pos1));
    
        pos1 = pos2 + c.size();
        pos2 = s.find(c, pos1);
    }
    if(pos1 != s.length())
        v.push_back(s.substr(pos1));
}

std::string Common::UnSplit(std::vector<std::string>& v, const std::string& c) {
    std::string str;
    int sz = v.size();
    for (int i = 0; i < sz; ++i) {
        str += v.at(i) + c;
    }
    return std::move(str);
}

std::wstring Common::UnSplit(std::vector<std::wstring>& v, const std::wstring& c) {
    std::wstring str;
    int sz = v.size();
    for (int i = 0; i < sz; ++i) {
        str += v.at(i) + c;
    }
    return std::move(str);
}

// erase the space of begin and end.
std::string Common::Trim(const std::string& s) {
    std::string str = s;
    if (str.empty()) return std::string();
    str.erase(0, str.find_first_not_of(" "));  
    str.erase(str.find_last_not_of(" ") + 1);  
    return std::move(str);
}

std::wstring Common::Trim(const std::wstring& s) {
    std::wstring str = s;
    if (str.empty()) return std::wstring();
    str.erase(0, str.find_first_not_of(L" "));  
    str.erase(str.find_last_not_of(L" ") + 1);  
    return std::move(str);
}

std::string Common::Base64Encode(const std::string& input) {
    std::string temp;
    temp.resize(modp_b64_encode_len(input.size()));  // makes room for null byte

    // modp_b64_encode_len() returns at least 1, so temp[0] is safe to use.
    size_t output_size = modp_b64_encode(&(temp[0]), input.data(), input.size());

    temp.resize(output_size);  // strips off null byte

    return std::move(temp);
}

std::wstring Common::Base64Encode(const std::wstring& input) {
    std::string tmp = Common::wstr2str(input);
    return Common::str2wstr(Common::Base64Encode(tmp));
}

std::string Common::Base64Decode(const std::string& input) {
    std::string temp;
    temp.resize(modp_b64_decode_len(input.size()));

    // does not null terminate result since result is binary data!
    size_t input_size = input.size();
    size_t output_size = modp_b64_decode(&(temp[0]), input.data(), input_size);
    if (output_size == MODP_B64_ERROR)
        return std::string();

    temp.resize(output_size);

    return std::move(temp);
}

std::wstring Common::Base64Decode(const std::wstring& input) {
    std::string tmp = Common::wstr2str(input);
    return Common::str2wstr(Common::Base64Decode(tmp));
}

std::string Common::ReadFile(const std::string& input) { 
    std::ifstream t(input);  
    std::string str((std::istreambuf_iterator<char>(t)),  
                 std::istreambuf_iterator<char>());
    return std::move(str);
}

std::wstring Common::ReadFile(const std::wstring& input) { 
    std::wifstream t(input);  
    std::wstring str((std::istreambuf_iterator<wchar_t>(t)),  
                 std::istreambuf_iterator<wchar_t>());
    return std::move(str);
}

bool Common::WriteFile(const std::string& input, const std::string& con) {
    std::ofstream file;
    file.open(input, std::ios::out);
    if (!file.is_open())
        return false;
    file << con;
    file.close();
    return true;
}

bool Common::WriteFile(const std::wstring& input, const std::wstring& con) {
    std::wofstream file;
    file.open(input, std::ios::out);
    if (!file.is_open())
        return false;
    file << con;
    file.close();
    return true;
}

UINT _GetCharCode(Common::encode_t char_codein) {
    switch (char_codein) {
        case Common::encode_t::utf8 : return CP_UTF8;
        case Common::encode_t::win_sys : return CP_ACP;
    }
    return CP_UTF8;
}

std::wstring Common::str2wstr(const std::string& from, Common::encode_t char_codein)
{
    UINT char_code = _GetCharCode(char_codein);
	if (from.length() == 0)
		return L"";
	int buflen = MultiByteToWideChar(char_code, 0, from.c_str(), -1, NULL, 0) + 1;
	if (buflen == 0)
		return L"";
	wchar_t * buf = new wchar_t[buflen];
	std::wstring retn = L"";
	if (buf) {
		memset(buf, 0, buflen * 2);
		MultiByteToWideChar(char_code, 0, from.c_str(), -1, buf, buflen);
		retn = buf;
		delete[]buf;
	}
	return std::move(retn);
}

std::string Common::wstr2str(const std::wstring& from, Common::encode_t char_codein)
{
    UINT char_code = _GetCharCode(char_codein);
	if (from.length() == 0)
		return "";

	std::string retn = "";
	try{
		int buflen = WideCharToMultiByte(char_code, 0, from.c_str(), -1, NULL, 0, NULL, NULL) + 1;
		if (buflen == 0)
			return "";
		char * buf = new char[buflen];
		if (buf != NULL){
			memset(buf, 0, buflen);
			WideCharToMultiByte(char_code, 0, from.c_str(), -1, buf, buflen, NULL, NULL);
			retn = buf;
			delete[]buf;
		}
	}
	catch (...) {

	}
	return std::move(retn);
}
